﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
using NASA.BeAMartian.TaskService;
using NASA.BeAMartian.Execution;
using NASA.BeAMartian.Extender;
using NASA.BeAMartian.Model;
using NASA.BeAMartian.DallasService;
using System.Windows.Media.Imaging;
using System.IO;
using NASA.BeAMartian.Utils;
using GalaxyZoo.Client.Model;
using Microsoft.Dallas.Services;
using System.ComponentModel;
using CraterCity.Service.Components.DataContract;

namespace NASA.BeAMartian.Views
{
    public partial class CountCraters : PhoneApplicationPage
    {
        private const bool TEST_MODE_ON = false;
        public event EventHandler<EventArgs> CountingFinished;

        // Constructor
        public CountCraters()
        {
            InitializeComponent();
            this.Loaded += new RoutedEventHandler(CountCraters_Loaded);
            this.CraterSelector.SelectorOpacity = this.OpacitySlider.Value;
        }

        void CountCraters_Loaded(object sender, RoutedEventArgs e)
        {
            this.LoadData();

            this.CraterSelector.CraterCountChanged += new EventHandler(CraterSelector_CraterCountChanged);
        }

        public bool IsProcessing
        {
            set
            {
                this.CraterSelector.IsProcessing = value;
            }
        }

        private void CraterSelector_CraterCountChanged(object sender, EventArgs e)
        {
            NASA.BeAMartian.Controls.CraterSelector selector = sender as NASA.BeAMartian.Controls.CraterSelector;
            this.CraterCountText.Text = selector.CountedCraters.ToString();
        }

        ExecutionQueue currentExecutionQueue = null;
        private ExecutionQueue CurrentExecutionQueue
        {
            get
            {
                if (this.currentExecutionQueue == null)
                {
                    this.currentExecutionQueue = new ExecutionQueue();
                }

                return this.currentExecutionQueue;
            }
        }

        private void CancelCurrentExecutionQueue()
        {
            if (this.currentExecutionQueue != null)
            {
                this.currentExecutionQueue.Cancel();
            }
        }

        private void LoadData()
        {
            if (TEST_MODE_ON)
            {
                LoadData(new Metadata
                {
                    Description = "",
                    ImageId = "",
                    X = 1
                });
            }
            else
            {

                TaskServiceClient client = new TaskServiceClient("BasicHttpBinding_ITaskService");
                ServiceClientExecutionItem<ITaskService, GetNextCountingTaskCompletedEventArgs> actionItem =
                        new ServiceClientExecutionItem<ITaskService, GetNextCountingTaskCompletedEventArgs>(
                            client,
                            () => client.GetNextCountingTaskAsync(),
                            (EventHandler<GetNextCountingTaskCompletedEventArgs> handler) => client.GetNextCountingTaskCompleted += handler,
                            (EventHandler<GetNextCountingTaskCompletedEventArgs> handler) => client.GetNextCountingTaskCompleted -= handler,
                            (s, e) => client_GetNextCountingTaskCompleted(s, e),
                            1,
                            () => this.CraterSelector.IsProcessing = true,
                            () => this.CraterSelector.IsProcessing = false
                        );

                this.CurrentExecutionQueue.Enqueue(actionItem);
                this.CurrentExecutionQueue.Run();
            }
        }

        private void client_GetNextCountingTaskCompleted(object sender, GetNextCountingTaskCompletedEventArgs e)
        {
            this.IsProcessing = false;

            if (e.Error != null)
                throw e.Error;

            if (e.Cancelled != true)
            {
                this.LoadData(e.Result);
            }
        }

        CraterTask task = null;

        private void LoadData(Metadata metadata)
        {
            if (TEST_MODE_ON)
            {
                (Resources["sbFadeInTitles"] as Storyboard).Begin();
            }
            else
            {
                this.LocationText.Text = ((MarsRegion)metadata.RegionId).ToString().PrefixUppercaseChars(" ").ToUpper();
                System.Diagnostics.Debug.WriteLine("Loading Crater: " + metadata.ImageId);

                Metadata[] metadataList = new Metadata[metadata.MaxTileX];

                for (int x = 1; x <= metadata.MaxTileX; x++)
                {
                    Metadata meta = new Metadata()
                    {
                        Description = metadata.Description,
                        ImageId = metadata.ImageId,
                        InstrumentId = metadata.InstrumentId,
                        LatitudeCenter = metadata.LatitudeCenter,
                        LatitudeMax = metadata.LatitudeMin,
                        LongitudeCenter = metadata.LongitudeCenter,
                        LongitudeMax = metadata.LongitudeMax,
                        LongitudeMin = metadata.LongitudeMin,
                        MapResolution = metadata.MapResolution,
                        MaxTileX = metadata.MaxTileX,
                        MaxTileY = metadata.MaxTileY,
                        RegionId = metadata.RegionId,
                        X = x,
                        Y = metadata.Y
                    };

                    metadataList[x - 1] = meta;
                    int xValue = x; // use this to make a separate capture instance for the delegate below

                    DallasServiceClient client = new DallasServiceClient("BasicHttpBinding_IDallasService");
                    ServiceClientExecutionItem<IDallasService, GetImageTileCompletedEventArgs> actionItem =
                        new ServiceClientExecutionItem<IDallasService, GetImageTileCompletedEventArgs>(
                            client,
                            () => client.GetImageTileAsync(((int)meta.InstrumentId).ToString(), ((int)meta.RegionId).ToString(), meta.MapResolution.ToString(), meta.ImageId, meta.X, meta.Y, xValue),
                            (EventHandler<GetImageTileCompletedEventArgs> handler) => client.GetImageTileCompleted += handler,
                            (EventHandler<GetImageTileCompletedEventArgs> handler) => client.GetImageTileCompleted -= handler,
                            (s, e) => client_GetImageTileBackgroundCompleted(s, e), 1,
                            () => this.CraterSelector.IsProcessing = true,
                            () => this.CraterSelector.IsProcessing = false
                        );

                    this.CurrentExecutionQueue.Enqueue(actionItem);
                    this.CurrentExecutionQueue.Run();
                }

                this.task = new CraterTask()
                {
                    CraterDefinitions = new List<CircularCraterDefinition>(),
                    MetadataList = metadataList,
                    BackgroundImages = new ImageSource[metadata.MaxTileX]
                };
            }
        }

        private void client_GetImageTileBackgroundCompleted(object sender, GetImageTileCompletedEventArgs e)
        {
            this.IsProcessing = false;

            if (e.Error != null)
                throw e.Error;

            if (e.Cancelled != true)
            {
                int xValue = (int)e.UserState;
                Metadata metadata = this.task.MetadataList[xValue - 1];
                BitmapImage bitmap = new BitmapImage();
                bitmap.SetSource(new MemoryStream(e.Result));
                WriteableBitmap wBitmap = new WriteableBitmap(bitmap);
                int max = bitmap.PixelWidth * bitmap.PixelHeight;
                bool hasNonBlack = false;

                for (int n = 0; n < max; n++)
                {
                    int pixel = wBitmap.Pixels[n];
                    byte r = (Byte)((pixel >> 16) & 0xFF);
                    byte g = (Byte)((pixel >> 8) & 0xFF);
                    byte b = (Byte)(pixel & 0xFF);
                    double lum = ColorUtils.GetLuminance(r, g, b);
                    if (lum > 20)
                    {
                        hasNonBlack = true;
                        break;
                    }
                }

                if (hasNonBlack)
                {
                    this.task.BackgroundImages[xValue - 1] = wBitmap;
                }
                else
                {
                    this.task.BackgroundImages[xValue - 1] = null;
                    this.task.MetadataList[xValue - 1] = null;
                }

                if (metadata.MaxTileX == xValue)
                {
                    this.CraterSelector.Populate(this.task);
                    (Resources["sbFadeInTitles"] as Storyboard).Begin();
                }
            }
        }


        private void Slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            Slider slider = (Slider)sender;
            if (this.CraterSelector != null)
            {
                this.CraterSelector.SelectorOpacity = slider.Value;
            }
        }

        private void DoneButton_Click(object sender, RoutedEventArgs args)
        {
            TaskData td = ApplicationContext.Instance.CraterTaskData;
            
            //default to anonymous user
            int userID = 772880;

            if (ApplicationContext.Instance.UserData != null && ApplicationContext.Instance.UserData.UserID != 0)
                userID = int.Parse(ApplicationContext.Instance.UserData.UserID.ToString());


            if (td == null)
            {
                ApplicationContext.Instance.CraterTaskData = new TaskData() { TaskCount = 1 };
            }
            else
            {
                ApplicationContext.Instance.CraterTaskData.TaskCount += 1;
            }

            this.task = this.CraterSelector.GetCraterTask();

            TaskServiceClient client = new TaskServiceClient("BasicHttpBinding_ITaskService");
            ServiceClientExecutionItem<ITaskService, SaveCraterTaskDataCompletedEventArgs> actionItem =
                new ServiceClientExecutionItem<ITaskService, SaveCraterTaskDataCompletedEventArgs>(
                    client,
                    () => client.SaveCraterTaskDataAsync(userID, new List<Metadata>(this.task.MetadataList), this.task.CraterDefinitions),
                    (EventHandler<SaveCraterTaskDataCompletedEventArgs> handler) => client.SaveCraterTaskDataCompleted += handler,
                    (EventHandler<SaveCraterTaskDataCompletedEventArgs> handler) => client.SaveCraterTaskDataCompleted -= handler,
                    (s, e) => client_SaveCraterTaskDataCompleted(s, e),
                    1,
                    () => this.CraterSelector.IsProcessing = true,
                    () => this.CraterSelector.IsProcessing = false
                );

            this.CurrentExecutionQueue.Enqueue(actionItem);
            this.CurrentExecutionQueue.Run();
        }


        private void client_SaveCraterTaskDataCompleted(object sender, SaveCraterTaskDataCompletedEventArgs e)
        {
            this.IsProcessing = false;

            if (e.Error != null)
                throw e.Error;

            if (e.Cancelled != true)
            {
                if (string.IsNullOrEmpty(e.Result.Error) == false)
                {
                    throw new InvalidOperationException(e.Result.Error);
                }
                ApplicationContext.Instance.CraterTaskData = e.Result;

                this.NavigationService.Navigate(new Uri("/Views/Participate/CountCratersTaskCompleted.xaml?x=1", UriKind.Relative));
            }
        }

        bool helpIsExpanded = false;

        private void Help_Click(object sender, RoutedEventArgs e)
        {
            if (helpIsExpanded)
            {
                FindDescendent(sender as DependencyObject).Fill = new SolidColorBrush(Colors.Transparent);
                (Resources["sbHelpCollapse"] as Storyboard).Begin();
                helpIsExpanded = false;
            }
            else
            {
                FindDescendent(sender as DependencyObject).Fill = ((SolidColorBrush)Application.Current.Resources["BackgroundColorTenBrush"]);
                (Resources["sbHelpExpand"] as Storyboard).Begin();
                helpIsExpanded = true;
            }
        }

        public static Rectangle FindDescendent(DependencyObject element)
        {
            Stack<FrameworkElement> stack = new Stack<FrameworkElement>();

            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++)
            {
                stack.Push((FrameworkElement)VisualTreeHelper.GetChild(element, i));
            }

            while (stack.Count > 0)
            {
                DependencyObject poppedElement = stack.Pop();
                Rectangle tPoppedElement = poppedElement as Rectangle;

                if (tPoppedElement != null)
                    return tPoppedElement;

                for (int i = 0; i < VisualTreeHelper.GetChildrenCount(poppedElement); i++)
                {
                    stack.Push((FrameworkElement)VisualTreeHelper.GetChild(poppedElement, i));
                }
            }

            return null;
        }

    }
}
